Day 11: 상속
상속은 기존 클래스(부모)의 속성과 기능을 새로운 클래스(자식)가 물려받는 것입니다. 코드 재사용성을 높이고, “is-a” 관계를 표현합니다. 예를 들어 “강아지는 동물이다(Dog is an Animal)” 관계를 상속으로 모델링할 수 있습니다.
기본 상속 구조
extends 키워드로 부모 클래스를 상속합니다.
// 부모 클래스
class Animal {
String name;
int age;
Animal(String name, int age) {
this.name = name;
this.age = age;
}
void eat() {
System.out.println(name + "이(가) 먹이를 먹습니다.");
}
void sleep() {
System.out.println(name + "이(가) 잠을 잡니다.");
}
void showInfo() {
System.out.println("이름: " + name + ", 나이: " + age + "살");
}
}
// 자식 클래스
class Dog extends Animal {
String breed;
Dog(String name, int age, String breed) {
super(name, age); // 부모 생성자 호출
this.breed = breed;
}
// 자식만의 메서드
void bark() {
System.out.println(name + "이(가) 멍멍! 짖습니다.");
}
void fetch(String item) {
System.out.println(name + "이(가) " + item + "을(를) 물어옵니다.");
}
}
class Cat extends Animal {
boolean isIndoor;
Cat(String name, int age, boolean isIndoor) {
super(name, age);
this.isIndoor = isIndoor;
}
void purr() {
System.out.println(name + "이(가) 그르릉 소리를 냅니다.");
}
}
public class InheritanceBasic {
public static void main(String[] args) {
Dog dog = new Dog("바둑이", 3, "진돗개");
dog.showInfo(); // 부모 메서드 사용 가능
dog.eat(); // 부모 메서드 사용 가능
dog.bark(); // 자식 고유 메서드
dog.fetch("공");
Cat cat = new Cat("나비", 2, true);
cat.showInfo();
cat.sleep();
cat.purr();
}
}
메서드 오버라이딩
부모 클래스의 메서드를 자식 클래스에서 재정의합니다. @Override 어노테이션으로 의도를 명시합니다.
class Shape {
String color;
Shape(String color) {
this.color = color;
}
double area() {
return 0;
}
void describe() {
System.out.println(color + " 도형, 넓이: " + area());
}
}
class Circle extends Shape {
double radius;
Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
double area() {
return Math.PI * radius * radius;
}
@Override
void describe() {
System.out.printf("%s 원 (반지름: %.1f), 넓이: %.2f%n",
color, radius, area());
}
}
class Rectangle extends Shape {
double width;
double height;
Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
double area() {
return width * height;
}
@Override
void describe() {
System.out.printf("%s 직사각형 (%.1f x %.1f), 넓이: %.2f%n",
color, width, height, area());
}
}
public class OverrideExample {
public static void main(String[] args) {
Circle circle = new Circle("빨간", 5.0);
Rectangle rect = new Rectangle("파란", 4.0, 6.0);
circle.describe(); // 빨간 원 (반지름: 5.0), 넓이: 78.54
rect.describe(); // 파란 직사각형 (4.0 x 6.0), 넓이: 24.00
}
}
super 키워드와 생성자 체인
super를 사용하여 부모의 생성자나 메서드를 호출합니다.
class Vehicle {
String brand;
int year;
int speed;
Vehicle(String brand, int year) {
this.brand = brand;
this.year = year;
this.speed = 0;
System.out.println("Vehicle 생성자 호출");
}
void accelerate(int amount) {
speed += amount;
System.out.println(brand + " 가속: 현재 속도 " + speed + "km/h");
}
void brake(int amount) {
speed = Math.max(0, speed - amount);
System.out.println(brand + " 감속: 현재 속도 " + speed + "km/h");
}
@Override
public String toString() {
return brand + " (" + year + ") - " + speed + "km/h";
}
}
class ElectricCar extends Vehicle {
int batteryLevel;
ElectricCar(String brand, int year, int batteryLevel) {
super(brand, year); // 부모 생성자 반드시 첫 줄에서 호출
this.batteryLevel = batteryLevel;
System.out.println("ElectricCar 생성자 호출");
}
@Override
void accelerate(int amount) {
if (batteryLevel <= 0) {
System.out.println("배터리가 부족합니다!");
return;
}
super.accelerate(amount); // 부모 메서드 호출
batteryLevel -= amount / 10;
System.out.println("배터리 잔량: " + batteryLevel + "%");
}
void charge() {
batteryLevel = 100;
System.out.println(brand + " 충전 완료!");
}
@Override
public String toString() {
return super.toString() + ", 배터리: " + batteryLevel + "%";
}
}
public class SuperExample {
public static void main(String[] args) {
ElectricCar tesla = new ElectricCar("Tesla Model 3", 2024, 80);
System.out.println(tesla);
tesla.accelerate(100);
tesla.accelerate(50);
System.out.println(tesla);
}
}
final과 Object 클래스
final 키워드로 상속이나 오버라이딩을 제한하고, 모든 클래스의 최상위 부모인 Object를 이해합니다.
// final 클래스: 상속 불가
// final class Immutable { }
// final 메서드: 오버라이딩 불가
class Parent {
final void cannotOverride() {
System.out.println("이 메서드는 오버라이딩할 수 없습니다.");
}
}
// Object 클래스 메서드 오버라이딩
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person other = (Person) obj;
return age == other.age && name.equals(other.name);
}
@Override
public int hashCode() {
return java.util.Objects.hash(name, age);
}
}
public class ObjectExample {
public static void main(String[] args) {
Person p1 = new Person("홍길동", 25);
Person p2 = new Person("홍길동", 25);
Person p3 = new Person("김영희", 30);
// toString 오버라이딩 효과
System.out.println(p1); // Person{name='홍길동', age=25}
// equals 오버라이딩 효과
System.out.println(p1.equals(p2)); // true (내용이 같음)
System.out.println(p1.equals(p3)); // false
// instanceof: 타입 확인
System.out.println(p1 instanceof Person); // true
System.out.println(p1 instanceof Object); // true (모든 객체)
}
}
오늘의 연습문제
-
직원 계층 구조:
Employee부모 클래스(이름, 급여)를 만들고,Manager(부서명, 보너스 비율)와Developer(프로그래밍 언어, 레벨) 자식 클래스를 만드세요. 각각calculatePay()메서드를 오버라이딩하세요. -
toString과 equals 구현:
Book클래스에toString(),equals(),hashCode()를 올바르게 오버라이딩하세요. 같은 ISBN을 가진 책은 동일한 책으로 판단합니다. -
도형 계층 구조:
Shape부모 클래스에area()와perimeter()메서드를 정의하고,Triangle(밑변, 높이, 세 변의 길이)과Trapezoid(윗변, 아랫변, 높이)를 상속으로 구현하세요.